package com.incubator.gwt.widgets;

import java.util.ArrayList;

import com.allen_sauer.gwt.log.client.Log;
import com.arisem.kim.webapp.client.images.ApplicationImages;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

/**
 * 
 * @author Slobodan
 * 
 * Simple list with add and remove options
 * 
 *  .hand - cursor style
 *  .editableList - widget style name
 *  .listItem - item style
 *  .listItem-Selected - style for selected item
 *
 */
public class EditableList<E> extends Composite{
	
	private FlowPanel panel;
	private FlowPanel insertPanel;
	private FlowPanel container;
	private ArrayList<EditableListItem<E>> items;
	private EditableListModel<E> model;
	private E selected;
	final Image plus = ApplicationImages.INSTANCE.add().createImage();

	
	public EditableList() {
		this(null);
	}
	
	public EditableList(EditableListModel<E> model) {
		this.model = model;
		panel = new FlowPanel();
		insertPanel = new FlowPanel();
		container = new FlowPanel();
		items = new ArrayList<EditableList<E>.EditableListItem<E>>();
		panel.getElement().getStyle().setPadding(5, Unit.PX);
		panel.add(container);
		panel.add(insertPanel);
		initWidget(panel);
		panel.setStyleName("editableList");
		createInsertForm();
	}
	
	private void createInsertForm(){
		HorizontalPanel hp = new HorizontalPanel();
		final TextBox insert = new TextBox();
		hp.add(insert);
		HTML glue = new HTML("&nbsp;");
		hp.add(glue);
		hp.setCellWidth(glue, "10px");
		hp.add(plus);
		plus.addStyleName("hand");
		final ClickHandler insertHandler = new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent arg0) {
				if(insert.getText().equals("")){
					return;
				}
				model.onAddItem(insert.getText(), new AsyncCallback<E>() {
					public void onSuccess(E item) {
						insert(item);
					};
					@Override
					public void onFailure(Throwable arg0) {
					}
				});
				insert.setText("");
			}
		};
		plus.addClickHandler(insertHandler);
		insert.addKeyUpHandler(new KeyUpHandler() {
			
			@Override
			public void onKeyUp(KeyUpEvent event) {
		        if (event.getNativeKeyCode() == 13) {
		        	insertHandler.onClick(null);
		        }
			}
		});
		hp.getElement().getStyle().setMarginTop(10, Unit.PX);
		insertPanel.add(hp);
	}
	
	public EditableListItem<E> insert(final E item){
		if(item != null){
			EditableListItem<E> listItem = new EditableListItem<E>(item, model){
				@Override
				public void onSelected() {
					if(selected != null){
						EditableListItem<E> widget = getEditableItem(selected);
						widget.setSelected(false);
					}
					selected = item;
					EditableListItem<E> widget = getEditableItem(selected);
					widget.setSelected(true);
					
					model.onSelected(item);
				};
				
				@Override
				public void onDelete() {
					removeFromList(item);
				}
			};
			listItem.setStyleName("listItem");
			items.add(listItem);
			container.add(items.get(items.size()-1));
			return listItem;
		}else{
			Log.error("## item cannot be null");
			return null;
		}
	}
	
	public void insert(E item, ClickHandler handler){
		EditableListItem<E> listItem = insert(item);
		listItem.addClickHandler(handler);
	}
	
	public void removeFromList(E item){
		int index = -1;
		for (EditableListItem<E> eitem : items) {
			if(eitem.getItem().equals(item)){
				index = items.indexOf(eitem);
				break;
			}
		}
		if(index != -1){
			items.remove(index);
			final int widgetIndex = index;
			DeferredCommand.addCommand(new Command() {
					@Override
					public void execute() {
						container.remove(widgetIndex);
					}
				});
		}else{
			Log.error("## "+item+" not found in the list");
		}
	}
	
	public void setModel(EditableListModel<E> model) {
		this.model = model;
	}
	
	public E getSelectedItem() {
		return selected;
	}
	
	public int getItemCount(){
		return items.size();
	}
	
	public void selectItem(int index){
		items.get(index).onSelected();
	}
	
	public EditableListItem<E> getEditableItem(E item){
		for (EditableListItem<E> ei: items) {
			if(ei.getItem().equals(item)){
				return ei;
			}
		}
		return null;
	}
	
	@SuppressWarnings("hiding")
	private abstract class EditableListItem<E> extends Composite implements HasClickHandlers{
		
		private FocusPanel panel;
		private HorizontalPanel container;
		private E item;
		private EditableListModel<E> model;
		private Label label;
		final Image close = ApplicationImages.INSTANCE.delete().createImage();

		public EditableListItem(E item, EditableListModel<E> model) {
			this.item = item;
			this.model = model;
			container = new HorizontalPanel();
			panel = new FocusPanel();
			panel.add(container);
			initWidget(panel);
			addItem(item);
		}
		
		private void addItem(final E item){
			label = new Label(model.getText(item));
			label.addStyleName("hand");
			label.addClickHandler(new ClickHandler() {
				
				@Override
				public void onClick(ClickEvent arg0) {
					onSelected();
				}
			});
			container.add(label);
			label.setWordWrap(false);
			HTML glue = new HTML("&nbsp;");
			container.add(glue);
			container.setCellWidth(label, "100%");
			close.addClickHandler(new ClickHandler() {
				@Override
				public void onClick(ClickEvent arg0) {
					onDelete();
					model.onDeleteItem(item);
				}
			});
			container.add(close);
			close.setStyleName("hand");
			close.setVisible(false);
			panel.addMouseOverHandler(new MouseOverHandler() {
				
				@Override
				public void onMouseOver(MouseOverEvent arg0) {
					if(!close.isVisible()){
						close.setVisible(true);
					}
				}
			});
			panel.addMouseOutHandler(new MouseOutHandler() {
				
				@Override
				public void onMouseOut(MouseOutEvent arg0) {
					if(close.isVisible()){
						close.setVisible(false);
					}
				}
			});
			
			label.addMouseOverHandler(new MouseOverHandler() {
				
				@Override
				public void onMouseOver(MouseOverEvent arg0) {
					label.addStyleName("listItem-Over");
				}
			});
			
			label.addMouseOutHandler(new MouseOutHandler() {
				
				@Override
				public void onMouseOut(MouseOutEvent arg0) {
					label.removeStyleName("listItem-Over");
				}
			});
			label.getElement().getStyle().setPaddingLeft(5, Unit.PX);
			
		}

		public E getItem() {
			return item;
		}
		
		public void setSelected(boolean selected){
			if(selected){
				label.addStyleName("listItem-Selected");
			}else{
				label.removeStyleName("listItem-Selected");
			}
		}

		@Override
		public HandlerRegistration addClickHandler(ClickHandler handler) {
			return label.addClickHandler(handler);
		}
		
		public abstract void onDelete();
		public abstract void onSelected();
		
	}


}
